package upbeat.frontend;
%%

%byaccj
%line
%column
%class Yylex

%{
  /* store a reference to the parser object */
  private parser yyparser;

  /* constructor taking an additional parser object */
  public Yylex(java.io.Reader r, parser yyparser) {
    this(r);
    this.yyparser = yyparser;
  }
  
  /* Useful debugging function */
  public String lexer_loc() {
	  return "line " + (yyline+1) + " col " + (yycolumn+1);
  }
  
  public int current_line() {
	  // for some reason, lines are 0-indexed!
	  return yyline+1;
  }
  
  private String bad_code(int fromhere) {
	  return new String( zzBuffer, zzStartRead, fromhere-zzStartRead );
  }
%}

/* REGULAR DEFINITIONS */
/* useful stuff */
lineterm =	\r|\n|\r\n
inchar =	[^\r\n]
delim =		{lineterm}|[ \t\f]
ws =		{delim}+
letter =	[A-Za-z]
digit =		[0-9]

/* comments */
comment = {TraditionalComment} | {EndOfLineComment}

TraditionalComment   = "/*" ~"*/"
EndOfLineComment     = "//" {inchar}* {lineterm}


/* tokens */
id =		{letter}({letter}|{digit})* | \$ 
int =		{digit}+
float =		{digit}+(\.{digit}+)
bool =		(TRUE|FALSE)
time =		@m{digit}+
string =	\"({letter}|{digit}|[ \._\/\\~\-])*\"

/* more useful stuff */
number =	{int}|{float}

%%

/* Whitespace, statement markers */
{ws}	{ /* do nothing */ }
{comment}	{ /* comment */ }
;		{ if (yyparser.yydebug) {
			System.out.print ("\n");
		  }
		  return yycharat(0); /* end of statement */
		}

/* Single Characters */
\{		{ return yycharat(0); }
\}		{ return yycharat(0); }
\(		{ return yycharat(0); }
\)		{ return yycharat(0); }
\[		{ return yycharat(0); }
\]		{ return yycharat(0); }
\=		{ return parser.EQ_ASS; }
\:		{ return yycharat(0); }
\|		{ return yycharat(0); }
\+		{ return yycharat(0); }
\-		{ return yycharat(0); }
\*		{ return yycharat(0); }
\/		{ return yycharat(0); }
\%		{ return yycharat(0); }
\.		{ return yycharat(0); }
\,		{ return yycharat(0); }
\!		{ return yycharat(0); }
/*\$		{ return yycharat(0); }*/
"<"			{ return parser.LESS_OP; }
">"			{ return parser.GREAT_OP; }

/* Operators, logic */
"<="		{ return parser.LE_OP; }
">="		{ return parser.GE_OP; }
"=="		{ return parser.EQ_OP; }
"!="		{ return parser.NE_OP; }
"and"		{ return parser.AND_OP; }
"or"		{ return parser.OR_OP; }
"in"		{ return parser.IN; }

/* Musical phrase demarcation */
"||"		{ return parser.DB; }
/*"||:"		{ return parser.LRPT; }*/
":||"		{ return parser.RRPT; }

/* Header tokens */
TIME_SIGNATURE	{ 
			if (yyparser.yydebug) {
				System.out.print("Found a TIME_SIGNATURE\n");
			}
			yyparser.yylval = new upbeatVal(4.0);
			return parser.TIME_SIGNATURE; }
TEMPO		{
			if (yyparser.yydebug) {
				System.out.print("Found a TEMPO\n");
			}
			return parser.TEMPO; }
			
/* Major code section tokens */
Add			{ return parser.ADD; }
Conductor	{ return parser.CONDUCTOR; }
Musician	{ return parser.MUSICIAN; }

/* Flow Control */
Case		{ return parser.CASE; }
Return		{ return parser.RETURN; }
Repeat		{ return parser.REPEAT; }
Break		{ return parser.BREAK; }
Continue	{ return parser.CONTINUE; }

/* Data Input accessors */
currentValue	{ return parser.CURRENT_VALUE; }
data		{ return parser.DATA; }
more		{ return parser.MORE; }
valueAt		{ return parser.VALUE_AT; }

/* Types! */
int			{ return parser.INT; }
float		{ return parser.FLOAT; }
bool		{ return parser.BOOL; }
cue			{ return parser.CUE; }
phrase		{ return parser.PHRASE; }
time		{ return parser.TIME; }

/* Special Identifiers */
"input"		{
	yyparser.yylval = new upbeatVal("input");
	return parser.INPUT; }
\$			{ 
	yyparser.yylval = new upbeatVal("cue");
	yyparser.yylval.type = upbeatVal.Type.ubCUE;
	yyparser.yylval.sval = "$";
	return parser.CURR_CUE; }


/* Musical operations */
Change		{ return parser.CHANGE; }
Volume		{ return parser.VOLUME; }
Instrument	{ return parser.INSTRUMENT; }
Pitch		{ return parser.PITCH; }
to			{ return parser.TO; }
by			{ return parser.BY; }


/* Constants */
{int}	{ int theint = Integer.parseInt(yytext());
		  yyparser.yylval = new upbeatVal(theint);
		  yyparser.yylval.code = "" + theint;
		  yyparser.yylval.type = upbeatVal.Type.ubINT;
			return parser.CONSTANT; }
{float}	{ double thefloat = Double.parseDouble(yytext());
		  yyparser.yylval = new upbeatVal(thefloat);
		  yyparser.yylval.code = "" + thefloat;
		  yyparser.yylval.type = upbeatVal.Type.ubFLOAT;
			return parser.CONSTANT; }
{bool}	{
		if (yytext().equals("TRUE")) {
			yyparser.yylval = new upbeatVal(true);
			yyparser.yylval.code = "true";
		} else /* must be false */ {
			yyparser.yylval = new upbeatVal(false);
			yyparser.yylval.code = "false";
		}
			if (yyparser.yydebug) {
				System.out.printf("Ran across %s which is %s!\n", yytext(), yyparser.yylval.bval);
			}
			yyparser.yylval.type = upbeatVal.Type.ubBOOL;
			return parser.CONSTANT; }
			
{time}	{
			String numpart = yytext().substring(2);
			yyparser.yylval = new upbeatVal(Integer.parseInt(numpart));
			yyparser.yylval.code = numpart;
			yyparser.yylval.type = upbeatVal.Type.ubTIME;
			return parser.CONSTANT;}
"REST"	{ yyparser.yylval = new upbeatVal(0);
// TODO: implement attributes for REST constant lexeme
			yyparser.yylval.type = upbeatVal.Type.ubPHRASE;
			return parser.CONSTANT;
			}

{string}	{ yyparser.yylval = new upbeatVal(yytext().substring(1, yytext().length()-1));
			yyparser.yylval.sval = yytext().substring(0);
			if (yyparser.yydebug) {
				System.out.printf("Got a string! %s\n", yytext());
			}
			yyparser.yylval.type = upbeatVal.Type.ubSTRING;
			return parser.STRING_LITERAL; }

/* Identifiers */
{id}	{
	String idname = "";
	if(yytext().equals("Conduct") || yytext().equals("currentMeasure")) {
		idname = yytext();
	} else {
		idname = "ub_zz_" + yytext();
		// for scoping, namespace will be distinct
	}
	yyparser.yylval = new upbeatVal(idname);
			// creates a new upbeatVal where the Java code equivalent will be the variable name
			// in a special namespace.
		  yyparser.yylval.sval = yytext();
		    // Using sval to stand for the original variable name.
// TODO: Do we need a symbol table?
			return parser.IDENTIFIER; }

/* This is a default rule to catch any code that was not going to make it. */
// TODO: Make the default lexing rule (which catches lexing errors) better.
.		{ throw new Error("Found something I shouldn't have: " + bad_code(zzCurrentPosL) + " at " + lexer_loc()); }
